package ch.ethz.fcl.overlays;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Projection;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PointF;
import android.util.FloatMath;
import android.view.MotionEvent;
import ch.ethz.fcl.draw.GeoBounds;
import ch.ethz.fcl.udp.Protocal;
import ch.ethz.fcl.udp.UDPClient;

public class CircleOverlay extends MetroBuzzOverlay {
	private String TAG = "Circle Overlay";
	private UDPClient client;
	private Paint paint;
	private int index;

	private GeoPoint geoCenterPoint;
	private GeoPoint geoOutPoint;
	private Point pointCenter = new Point();
	private Point pointOut = new Point();

	private PointF start = new PointF();
	private PointF mid = new PointF();
	private PointF old = new PointF();

	private String mode;

	private float oldDist = 0;
	public static boolean cylinder = true;
	boolean hitCircle = false;

	String msg = "MetroBuzz:Control:";

	public CircleOverlay(Context context, int index) {
		super(context);
		this.index = index;

		TAG += index;
		client = new UDPClient();
		client.run();
	}

	public void init(GeoPoint geoCenter, GeoPoint geoOut) {
		this.geoOutPoint = new GeoPoint(geoOut.getLatitudeE6(),
				geoOut.getLongitudeE6());
		this.geoCenterPoint = new GeoPoint(geoCenter.getLatitudeE6(),
				geoCenter.getLongitudeE6());

		paint = new Paint();
		paint.setColor(Color.BLUE);
		paint.setStyle(Paint.Style.FILL);
		paint.setAlpha(80);
	}

	@Override
	public void draw(Canvas canvas, MapView mapView, boolean shadow) {
		Projection proj = mapView.getProjection();
		pointCenter = proj.toPixels(geoCenterPoint, pointCenter);
		pointOut = proj.toPixels(geoOutPoint, pointOut);

		canvas.drawCircle(pointCenter.x, pointCenter.y,
				disGeoPoint(mapView, geoCenterPoint, geoOutPoint), paint);
		// Log.d(TAG, "Center: Lat: " + geoCenterPoint.getLatitudeE6() +
		// " Lon: "
		// + geoCenterPoint.getLongitudeE6());
		// Log.d(TAG, "Map Center: Lat: " +
		// mapView.getMapCenter().getLatitudeE6() + " Lon: "
		// + mapView.getMapCenter().getLongitudeE6());
	}

	@Override
	public boolean onTouchEvent(MotionEvent event, MapView mapView) {
		final int action = event.getAction();
		boolean result = false;
		Projection proj = mapView.getProjection();
		switch (action & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:
			if (hitCircle(event, mapView)) {
				start.x = event.getX();
				start.y = event.getY();
				hitCircle = true;
				result = true;
				mode = Protocal.DRAG;
			}
			break;
		case MotionEvent.ACTION_UP:
		case MotionEvent.ACTION_POINTER_UP:
			if (hitCircle) {
				mode = Protocal.FINGERUP;
				hitCircle = false;
				result = true;
				client.setMessage(msg + Protocal.FINGERUP + ":"
						+ Protocal.cOperation + ":" + index);
				client.run();
			}
			break;
		case MotionEvent.ACTION_MOVE:
			if (hitCircle) {
				if (mode == Protocal.DRAG) {
					float xDiff = event.getX() - start.x;
					float yDiff = event.getY() - start.y;
					GeoPoint eventGeo = (GeoPoint) proj.fromPixels(
							(int) event.getX(), (int) event.getY());
					GeoPoint eventStart = (GeoPoint) proj.fromPixels(
							(int) start.x, (int) start.y);
					start.set(event.getX(), event.getY());
					if (FloatMath.sqrt(xDiff * xDiff + yDiff * yDiff) > 10.0f) {
						int centerLat = geoCenterPoint.getLatitudeE6()
								+ eventGeo.getLatitudeE6()
								- eventStart.getLatitudeE6();
						int centerLong = geoCenterPoint.getLongitudeE6()
								+ eventGeo.getLongitudeE6()
								- eventStart.getLongitudeE6();

						geoCenterPoint = new GeoPoint(centerLat, centerLong);

						// geoCenterPoint.setLatitudeE6(geoCenterPoint
						// .getLatitudeE6()
						// + eventGeo.getLatitudeE6()
						// - eventStart.getLatitudeE6());
						// geoCenterPoint.setLongitudeE6(geoCenterPoint
						// .getLongitudeE6()
						// + eventGeo.getLongitudeE6()
						// - eventStart.getLongitudeE6());

						int outLat = geoOutPoint.getLatitudeE6()
								+ eventGeo.getLatitudeE6()
								- eventStart.getLatitudeE6();

						int outLong = geoOutPoint.getLongitudeE6()
								+ eventGeo.getLongitudeE6()
								- eventStart.getLongitudeE6();

						// geoOutPoint.setLatitudeE6(geoOutPoint.getLatitudeE6()
						// + eventGeo.getLatitudeE6()
						// - eventStart.getLatitudeE6());
						// geoOutPoint.setLongitudeE6(geoOutPoint.getLongitudeE6()
						// + eventGeo.getLongitudeE6()
						// - eventStart.getLongitudeE6());

						geoOutPoint = new GeoPoint(outLat, outLong);
						client.setMessage(msg + Protocal.DRAG + ":"
								+ Protocal.cOperation + ":" + index + ":"
								+ info());
					}
				} else if (mode.equals(Protocal.ZOOM)) {
					float newDist = spacing(event);
					if (newDist > 10.0f && Math.abs(newDist - oldDist) > 10.0f) {
						GeoPoint eventGeo0 = (GeoPoint) proj.fromPixels(
								(int) event.getX(0), (int) event.getY(0));
						GeoPoint eventGeo1 = (GeoPoint) proj.fromPixels(
								(int) event.getX(1), (int) event.getY(1));

						if (newDist > oldDist) {
							GeoPoint geoDiff = new GeoPoint(Math.abs(eventGeo1
									.getLatitudeE6()
									- eventGeo0.getLatitudeE6()),
									Math.abs(eventGeo1.getLongitudeE6()
											- eventGeo0.getLongitudeE6()));
							radiusChange(geoDiff);
							client.setMessage(msg + Protocal.ZOOMOUT + ":"
									+ Protocal.cOperation + ":" + index + ":"
									+ info());
						} else {
							GeoPoint geoDiff = new GeoPoint(-Math.abs(eventGeo0
									.getLatitudeE6()
									- eventGeo1.getLatitudeE6()),
									-Math.abs(eventGeo0.getLongitudeE6()
											- eventGeo1.getLongitudeE6()));

							radiusChange(geoDiff);
							client.setMessage(msg + Protocal.ZOOMIN + ":"
									+ Protocal.cOperation + ":" + index + ":"
									+ info());
						}
					}
					oldDist = newDist;
				}
				result = true;
				client.run();
			}
			break;
		case MotionEvent.ACTION_POINTER_DOWN:
			if (hitCircle) {
				oldDist = spacing(event);
				// pivot.set(event.getX(0), event.getY(0));
				old.set(event.getX(1), event.getY(1));
				if (oldDist > 10.0f) {
					midPoint(mid, event);
					mode = Protocal.ZOOM;
				}
				result = true;
			}
		}
		mapView.postInvalidate();
		return (result || super.onTouchEvent(event, mapView));
	}

	private boolean hitCircle(MotionEvent event, MapView view) {
		Projection proj = view.getProjection();

		GeoPoint touch = (GeoPoint) proj.fromPixels((int) event.getX(),
				(int) event.getY());
		float dist = disGeoPoint(view, geoCenterPoint, touch);
		float radius = disGeoPoint(view, geoCenterPoint, geoOutPoint);
		float r = radius > 100f ? radius : 100.0f;
		return dist < r;
	}

	private float spacing(MotionEvent event) {
		float x = event.getX(0) - event.getX(1);
		float y = event.getY(0) - event.getY(1);
		return FloatMath.sqrt(x * x + y * y);
	}

	private void midPoint(PointF point, MotionEvent event) {
		float x = event.getX(0) + event.getX(1);
		float y = event.getY(0) + event.getY(1);
		point.set(x / 2, y / 2);
	}

	private float disGeoPoint(MapView view, GeoPoint center, GeoPoint out) {
		Projection proj = view.getProjection();
		Point p1 = new Point();
		proj.toPixels(center, p1);

		Point p2 = new Point();
		proj.toPixels(out, p2);

		int sq_x = (p1.x - p2.x) * (p1.x - p2.x);
		int sq_y = (p1.y - p2.y) * (p1.y - p2.y);

		return (float) Math.sqrt(sq_x + sq_y);
	}

	private void radiusChange(GeoPoint geo) {
		int lat = geoOutPoint.getLatitudeE6();
		int lon = geoOutPoint.getLongitudeE6();
		if (geoCenterPoint.getLatitudeE6() < geoOutPoint.getLatitudeE6())
			lat += 0.1 * geo.getLatitudeE6();
		// geoOutPoint
		// .setLatitudeE6((int) (0.1 * geo.getLatitudeE6() + geoOutPoint
		// .getLatitudeE6()));

		else if (geoCenterPoint.getLatitudeE6() > geoOutPoint.getLatitudeE6())
			lat -= 0.1 * geo.getLatitudeE6();
		// geoOutPoint
		// .setLatitudeE6((int) (geoOutPoint.getLatitudeE6() - 0.1 * geo
		// .getLatitudeE6()));

		if (geoCenterPoint.getLongitudeE6() < geoOutPoint.getLongitudeE6())
			lon += 0.1 * geo.getLongitudeE6();
		// geoOutPoint
		// .setLongitudeE6((int) (geoOutPoint.getLongitudeE6() + 0.1 * geo
		// .getLongitudeE6()));

		else if (geoCenterPoint.getLatitudeE6() > geoOutPoint.getLatitudeE6())
			lon -= 0.1 * geo.getLongitudeE6();
		// geoOutPoint
		// .setLongitudeE6((int) (geoOutPoint.getLongitudeE6() - 0.1 * geo
		// .getLongitudeE6()));
		geoOutPoint = new GeoPoint(lat, lon);
	}

	private String info() {
		float centerY = GeoBounds.getBoundLat(geoCenterPoint.getLatitudeE6());
		float centerX = GeoBounds.getBoundLon(geoCenterPoint.getLongitudeE6());

		float outY = GeoBounds.getBoundLat(geoOutPoint.getLatitudeE6());
		float outX = GeoBounds.getBoundLon(geoOutPoint.getLongitudeE6());

        return centerX + ":" + centerY + ":" + outX + ":" +outY;
	}

	public void sendInfo() {
		client.setMessage(msg + Protocal.DRAG + ":" + Protocal.cOperation + ":"
				+ index + ":" + info());
		client.run();
	}

	public void sendFingerUp() {
		client.setMessage(msg + Protocal.FINGERUP + ":" + Protocal.cOperation
				+ ":" + index);
		client.run();
	}
}
